#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <pthread.h>
#include <malloc.h>

#include "app.h"
#include "log.h"
#include "communication.h"

static int analy_head(char data, int cmd);
static void send_ackok(int sendfd);
static char check_lrc(const char* pData, int nL);

/**
 * @brief	初始化通信    
 */
int sever_communication_init(void)
{
    int ret = 0;
    int serverfd;
    struct sockaddr_in server_addr;

    serverfd = socket(AF_INET, SOCK_STREAM, 0);
    if(serverfd == -1) {
        DEBUG("socket error\r\n");
        return (-COMM_GET_FD_ERR);
    }

    //socket和端口、地址进行绑定
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    server_addr.sin_addr.s_addr = INADDR_ANY;
    bzero(&(server_addr.sin_zero), 8);
    ret = bind(serverfd, (struct sockaddr*)&server_addr, 
                         sizeof(struct sockaddr));
    if(ret == -1) {
        DEBUG("bind error\r\n");
        return (-COMM_BIND_ERR);
    }

    ret = listen(serverfd, BACKLOG);
    if(ret == -1) {
        DEBUG("listen error");
        return (-COMM_LISTEN_ERR);
    }

    return serverfd;
}


/**
 * @brief	接受数据，并进行分析
 */
int recv_data(int recvfd, char **data, uint16 *length)
{
    char recv_status = ANALY_HEAD;
    char recv_buff[4] = { 0 };
    char recv_tmp = 0;
    int recv_count = 0;
    char ret = 0;
    char recv_crc = 0;
    uint16 recv_length = 0;

    /* 日常判断入口参数 */
    if (!data || !length) {
        DEBUG("PARAM ERROR!\r\n");
        return (-COMM_PARAM_NULL);
    }

    while (1) {
        /* 每次循环读取一个字节的数据进行判断 */
        ret = recv(recvfd, &recv_tmp, 1, 0);
        if (ret == -1) {
            DEBUG("RECV　DATA　ERROR\r\n");
            return (-COMM_RECV_BYTE_ERR);
        }
        if (recv_status == ANALY_HEAD || recv_status == ANALY_LEN)
            recv_buff[recv_count] = recv_tmp;

        recv_count++;
        

        switch (recv_status) {
            /*判断帧头是否正确，不正确返回对应的ACK*/
            case ANALY_HEAD:
                if (recv_count == 1) {
                    if (recv_tmp == 0) {
                        sem_post(&sem);
                        return COMM_GET_ACK;
                    } else if (recv_tmp == COMM_CLIENT_DISCONNECT) {
                        return COMM_CLIENT_DISCONNECT;
                    }
                    ret = analy_head(recv_tmp, recv_count);
                    if (ret != TRUE ) {
                        send_ack(recvfd, ACK_RECV_HEAD_FIRST_ERR);
                        DEBUG("SEND　ACK_ERROR : %02x \r\n", ACK_RECV_HEAD_FIRST_ERR);
                        return (-COMM_ANAYL_HEAD_FIRST_ERR);
                    }
                }

                if (recv_count == 2) {
                    ret = analy_head(recv_tmp, recv_count);
                    if (ret != TRUE) {
                        send_ack(recvfd, ACK_RECV_HEAD_SECOND_ERR);
                        DEBUG("SEND　ACK_ERROR : %02x \r\n", ACK_RECV_HEAD_SECOND_ERR);
                        return (-COMM_ANAYL_HEAD_SECOND_ERR);
                    }
                    else
                        recv_status = ANALY_LEN;
                }
                break;

            /*分析数据长度*/
            case ANALY_LEN:
                if (recv_count == 4) {
                    recv_length = BUILD_UINT16(recv_buff[3], recv_buff[2]);
                    if (recv_length > 0) {
                        *length = recv_length;
                        recv_status = ANALY_DATA;
                        *data = (char *)malloc(recv_length);
                        DEBUG("recv_length = %d\r\n", recv_length);
                        recv_count = 0;
                    }
                    else {
                        send_ack(recvfd, ACK_RECV_LENGTH_ERR);
                        DEBUG("SEND　ACK_ERROR : %02x \r\n", ACK_RECV_LENGTH_ERR);
                        return (-COMM_ANAYL_LENGTH_ERR); 
                    }
                }
                break;

            /*接收并分析数据*/
            case ANALY_DATA:
                /*接收有效数据*/
                if (recv_count <= recv_length)
                    (*data)[recv_count - 1] = recv_tmp;

                /*对校验位进行校验*/
                if (recv_count == (recv_length + 1)) {
                    recv_crc = check_lrc((char *)*data, recv_length);
                    if (recv_crc == recv_tmp) {
                        recv_status = ANALY_DONE;
                        if ((*data)[1] != HEART_CMD) {
                            send_ackok(recvfd);
                        }
                        DEBUG("RECV　SUCCESS !\r\n");
                    } else {
                        send_ack(recvfd, ACK_RECV_LRC_ERR);
                        DEBUG("SEND　ACK_ERROR : %02x \r\n", ACK_RECV_LRC_ERR);
                        free(*data);
                        return (-COMM_ANAYL_LRC_ERR); 
                    }
                }
                break;

            default:
                break;
        }

        /*数据已经接受完毕*/
        if (recv_status == ANALY_DONE) {
            break;
        }
           
    }

    return SUCCESS;
}

/**
 * @brief	发送数据
 */
int send_data(int sendfd, char *data, uint16 length)
{
    char *packed_data;
    int ret = 0;
    int count = 0;

    if (!data) {
        DEBUG("PARAM ERROR!\r\n");
        return (-COMM_PARAM_NULL);
    }

    /*长度为帧头加数据长度4个字节+数据+校验位*/
    packed_data = (char *)malloc(length + 4 + 1);

    packed_data[0] = DATA_HEADER_FIRST;
    packed_data[1] = DATA_HEADER_SECOND;
    packed_data[2] = HI_UINT16(length);
    packed_data[3] = LO_UINT16(length);

    /*这里不能用strncpy，用为这里的数据中间包含结束符，strncpy
        会将结束符后面的数据替换成结束符*/
    for (count = 0; count < length; count++)
        packed_data[4 + count] = data[count];

    /*计算出校验位*/
    packed_data[length + 4] = check_lrc((char *)data, length);

    ret = send(sendfd, packed_data, length + 4 + 1, 0);
    if (ret == -1) {
        DEBUG("SEND　DATA　ERROR\r\n");
        return (-COMM_SEND_ERR);
    }
    
    sem_wait(&sem);
    DEBUG("SEND DATA SUCCESS !\r\n");
    free(packed_data);
    return TRUE;
}



/**
 * @brief	分析数据
 */
int analy_data(struct data_packet *p_data_packet, 
                char *data, uint16 length) 
{
    int length_temp = 0;
    int length_sum = 0;
    int count = 0;

    if (!p_data_packet || !data) {
        DEBUG("PARAM ERROR!\r\n");
        return (-COMM_PARAM_NULL);
    }

    for (count = 0; count < DATA_MAX_NUM; count++) {
        if (p_data_packet->data[count]) {
            free(p_data_packet->data[count]);
            p_data_packet->data[count] = NULL;
        }
    }
    count = 0;

    p_data_packet->cmd_type = data[0];
    p_data_packet->cmd = data[1];
    p_data_packet->ack = data[2];

    while (length_sum < length - 3) {
        length_temp = strlen((char *)&data[3 + length_sum]) + 1;
        if (length_temp > length) {
            DEBUG("ANALY DATA ERROR : TOO LONG");
            return (-COMM_DATA_FIELD_LENGTH_ERR);
        }

        p_data_packet->data[count] = (char *)malloc(length_temp);
        strncpy((char *)p_data_packet->data[count],
                (char *)&data[3 + length_sum], length_temp);

        count++;
        length_sum += length_temp;
    }

    return SUCCESS;
}


/**
 * @brief	打包数据
 */
int package_data(struct data_packet *p_data_packet, 
                char **data, uint16 *length) 
{
    int count = 0;
    int length_sum = 0;
    int length_temp = 0;

    if (!p_data_packet || !data || !length) {
        DEBUG("PARAM ERROR!\r\n");
        return (-COMM_PARAM_NULL);
    }

    for (count = 0; count < DATA_MAX_NUM; count++) {
        if (p_data_packet->data[count])
        /* 这里加1是算上\0 */
        length_sum += (strlen((char *)p_data_packet->data[count]) + 1);
    }
    count = 0;

    /* 算上命令类型、命令、和结果位*/
    length_sum += 3;
    *length = length_sum;
    *data = (char *)malloc(length_sum);

    (*data)[0] = p_data_packet->cmd_type;
    (*data)[1] = p_data_packet->cmd;
    (*data)[2] = p_data_packet->ack;

    length_sum = 0;
    for (count = 0; count < DATA_MAX_NUM; count++) {
        if (p_data_packet->data[count]) {

            length_temp = strlen((char *)p_data_packet->data[count]) + 1;
            strncpy((char *)&(*data)[3 + length_sum],
                (char *)p_data_packet->data[count], length_temp);

            length_sum += length_temp;
        }
    }

    return SUCCESS;
}


/**
 * @brief	分析数据的帧头是否正确   
 */
int analy_head(char data, int cmd)
{
    if (cmd == 1) {
        if (data == DATA_HEADER_FIRST)
            return TRUE;
        else
            return FAIL;
    }

    if (cmd == 2) {
        if (data == DATA_HEADER_SECOND)
            return TRUE;
        else
            return FAIL;
    }

    return FAIL;
}

/**
 * @brief	发送ACK_OK  
 */
void send_ackok(int sendfd)
{
    int ret = 0;
    char data;

    data = 1;
    DEBUG("SEND　ACK_OK\r\n");
    ret = send(sendfd, &data, 1, 0);
    if (ret == -1)
         DEBUG("SEND　ACK_OK　FAIL\r\n");
}


/**
 * @brief	发送不同的ACK
 */
void send_ack(int sendfd, char error)
{
    int ret = 0;

    ret = send(sendfd, &error, 1, 0);
    if (ret == -1)
         DEBUG("SEND　ACK_ERR　FAIL\r\n");
}

/**
 * @brief	LRC校验     
 */
static char check_lrc(const char* pdata,int nl)
{
    int i;
    char chlrc=0;

    if (!pdata) {
        DEBUG("PARAM ERROR!\r\n");
        return (-COMM_PARAM_NULL);
    }
    
    for (i = 0 ; i < nl; i++)
    {
       chlrc ^= pdata[i];
    }
    return chlrc;
}